package com.javasm.sale.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.javasm.commons.entity.CrmProperties;
import com.javasm.commons.util.Constants;
import com.javasm.commons.util.OssUtil;
import com.javasm.dict.service.DicDataService;
import com.javasm.gg.Goods;
import com.javasm.gg.GoodsService;
import com.javasm.sale.CommodityOrder;
import com.javasm.sale.Order;
import com.javasm.sale.dao.OrderDao;
import com.javasm.sale.service.CommodityOrderService;
import com.javasm.sale.service.OrderService;
import com.javasm.sys.CurrentLogin;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.beans.Transient;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderDao, Order> implements OrderService {


    @Override
    public List<Order> listOrder(Order order) {
        return baseMapper.listOrder(order);
    }

    @Override
    public List<Order> listOrderAndIMEA(Map<String, String> map) {
        List<Order> orders = baseMapper.listOrderAndIMEA(map);
        for (Order order : orders) {
            String signForTime = order.getSignForTime();
            if (!StringUtils.hasLength(signForTime)) continue;
            Date signForTimeDate = DateUtil.parse(signForTime);
            Date nowDate = new Date();
            //相差时间
            long betweenDay = DateUtil.between(signForTimeDate, nowDate, DateUnit.DAY);
            if (betweenDay > 7) {
                order.setSalesReturn(false);
            }
        }
        return orders;
    }

    //模板文件下载
    @Override
    public byte[] excelTemplate(String objectName) throws IOException {
        InputStream in = OssUtil.download(CrmProperties.getOss().getStandBuckName(), objectName);
        byte[] bytes = IoUtil.readBytes(in);
        if (!Constants.ORDER_EXCEL_TEMPLATE.equals(objectName))
            OssUtil.del(CrmProperties.getOss().getStandBuckName(), objectName);
        return bytes;
    }


    @Resource
    private DicDataService dictEntryService;

    private ReadSheet readExcelSheetOrder(List<Order> errors, int[] counts, List<String> currentOrderId, List<String> sucOrderId ) {
        //查询出相关字典项做数据转换使用 mapByDictType(String dictType
        Map<String, String> orderStatusMap = dictEntryService.mapByDictTypeKeyIsEntryTag(Constants.ORDER_STATUS); //订单状态
        Map<String, String> distributionMap = dictEntryService.mapByDictTypeKeyIsEntryTag(Constants.DISTRIBUTION); //订单配送方式
        Map<String, String> salePayMap = dictEntryService.mapByDictTypeKeyIsEntryTag(Constants.SALE_PAY); //订单支付方式
        Map<String, String> saleOrderTypeMap = dictEntryService.mapByDictTypeKeyIsEntryTag(Constants.SALE_ORDER_TYPE); //订单类型

        return EasyExcel.readSheet(0).head(Order.class).registerReadListener(new ReadListener<Order>() {
            public static final int BATCH_COUNT = 100;
            private List<Order> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(Order data, AnalysisContext context) {
                counts[0]++;
                //对data对象做数据校验
                boolean isok = data.check();
                if (!isok) {
                    errors.add(data);
                    return;
                }

                //校验通过后去重
                isok = currentOrderId.contains(data.getOrderId());
                if (isok) {
                    data.setErrorMsg("订单编号已存在");
                    errors.add(data);
                    return;
                } else {
                    currentOrderId.add(data.getOrderId());
                }

                //转义
                isok = data.parse(orderStatusMap, distributionMap, salePayMap, saleOrderTypeMap);
                if (!isok) {
                    errors.add(data);
                    return;
                }

                //全部通过
                data.setCreateId(CurrentLogin.getLoginUser().getUserId()+"");
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            private void saveData() {
                boolean b = saveBatch(cachedDataList);
                if (!b) {
                    errors.addAll(cachedDataList);
                } else {
                    counts[1] += cachedDataList.size();
                    cachedDataList.forEach( order ->sucOrderId.add(order.getOrderId()));

                }
            }
        }).build();
    }

    @Transient
    @Override
    public Map<String, Object> readExcel(InputStream inputStream) throws FileNotFoundException {
        //查询出所有订单id 做数据查重使用
        List<String> currentOrderId = list().stream().map(Order::getOrderId).collect(Collectors.toList());

        //存储订单页失败记录
        List<Order> orderErrors = new ArrayList<>();
        //存储订单页导入条数
        final int[] orderCounts = {0, 0};//总记录数,成功入库数

        //存储正确的订单的id
        List<String> sucOrderId = new ArrayList<>();

        //存储订单商品页失败记录
        List<CommodityOrder> orderCommodityErrors = new ArrayList<>();
        //存储订单商品页导入条数
        final int[] orderCommodityCounts = {0, 0};//总记录数,成功入库数

        try (ExcelReader excelReader = EasyExcel.read(inputStream).build()) {
            ReadSheet readSheet1 = readExcelSheetOrder(orderErrors, orderCounts, currentOrderId,sucOrderId);
            ReadSheet readSheet2 =
                    readExcelSheetOrderCommodity(orderCommodityErrors, orderCommodityCounts, currentOrderId,sucOrderId);

            excelReader.read(readSheet1, readSheet2);
        }

        //excel读取完成.
        Map<String, Object> msg = new HashMap<>();

        if (orderErrors.size() > 0 || orderCommodityErrors.size() > 0) {
            String fileName = "订单excel导入失败.xlsx";
            String filePath = excelParseToFile(fileName, orderErrors, orderCommodityErrors,true);
            String currentDate = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
            //使用随机id在确保文件名不会重复
            String randomFileName = IdUtil.objectId() + fileName;
            String objectName = "mg/errorExcel/" + currentDate + "/" + randomFileName;
            OssUtil.uploadFile(CrmProperties.getOss().getStandBuckName(), objectName, new FileInputStream(filePath));
            new File(filePath).delete();
            msg.put("objectName", objectName);
        }
        msg.put("orderNums", orderCounts[0]);
        msg.put("orderSucNums", orderCounts[1]);
        msg.put("orderCommodityNums", orderCommodityCounts[0]);
        msg.put("orderCommoditySucNums", orderCommodityCounts[1]);
        return msg;
    }


    //往excel中添加 订单数据和它包含的商品数据
    private String excelParseToFile(String fileName, List<Order> orders, List<CommodityOrder> orderCommoditys, boolean isError) {
        String root = CrmProperties.getRoot();
        String tempPath = root + "/temp";
        File tempFilePath = new File(tempPath);
        if (!tempFilePath.exists()) tempFilePath.mkdirs();
        String filePath = tempPath + "/" + fileName;
//        fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 指定文件
        try (ExcelWriter excelWriter = EasyExcel.write(filePath).build()) {
            // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变，我这里为了方便 所以用的同一个class
            // 实际上可以一直变
            WriteSheet writeSheet = orderWriteSheet(isError);
            excelWriter.write(orders, writeSheet);
            WriteSheet writeSheet1 =commodityOrderWriteSheet();
            excelWriter.write(orderCommoditys, writeSheet1);
        }
        return filePath;
    }

   private  WriteSheet commodityOrderWriteSheet(){
       Set<String> commodityOrderExcludeColumnFiledNames = new HashSet<String>();
       commodityOrderExcludeColumnFiledNames.add("createTime");
       commodityOrderExcludeColumnFiledNames.add("updateTime");
       return EasyExcel.writerSheet(1, Constants.COMMODITY_ORDER_EXCEL_SHEEL).head(CommodityOrder.class).excludeColumnFieldNames(commodityOrderExcludeColumnFiledNames).build();
   }

   private  WriteSheet orderWriteSheet(boolean isError){
       Set<String> excludeColumnFiledNames = new HashSet<String>();
       excludeColumnFiledNames.add("createId");
       excludeColumnFiledNames.add("createTime");
       excludeColumnFiledNames.add("updateTime");
       excludeColumnFiledNames.add("commodityList");
       excludeColumnFiledNames.add("createTimeArray");
       excludeColumnFiledNames.add("isSalesReturn");
       excludeColumnFiledNames.add("delivery");
       if (!isError) excludeColumnFiledNames.add("errorMsg");

      return EasyExcel.writerSheet(0, Constants.Order_EXCEL_SHEEL).head(Order.class).excludeColumnFieldNames(excludeColumnFiledNames).build();

   }


    //把查询结果 放入到excel中
    @Override
    public byte[] queryListToExcel(List<Order> orders) throws FileNotFoundException {
        Map<String, String> orderStatusMap = dictEntryService.mapByDictTypeKeyIsEntryValue(Constants.ORDER_STATUS); //订单状态
        Map<String, String> distributionMap = dictEntryService.mapByDictTypeKeyIsEntryValue(Constants.DISTRIBUTION); //订单配送方式
        Map<String, String> salePayMap = dictEntryService.mapByDictTypeKeyIsEntryValue(Constants.SALE_PAY); //订单支付方式
        Map<String, String> saleOrderTypeMap = dictEntryService.mapByDictTypeKeyIsEntryValue(Constants.SALE_ORDER_TYPE); //订单类型

        ArrayList<CommodityOrder> commodities = new ArrayList<>();

        orders.forEach(order -> {
            order.setStatus(orderStatusMap.get(order.getStatus()));
            order.setDistribution(distributionMap.get(order.getDistribution()));
            order.setPay(salePayMap.get(order.getPay()));
            order.setOrderType(saleOrderTypeMap.get(order.getOrderType()));
            order.getCommodityList().forEach(commodity -> commodity.setOrderId(order.getOrderId()));
            commodities.addAll(order.getCommodityList());
        });


        String fileName = "查询订单结果.xlsx";
        String filePath = queryExcelParseToFile(fileName, orders,commodities, false);
        FileInputStream fileInputStream = new FileInputStream(filePath);
        byte[] bytes = IoUtil.readBytes(fileInputStream);
        new File(filePath).delete();
        return bytes;
    }


    //往excel中添加 订单数据和它包含的商品数据
    private String queryExcelParseToFile(String fileName, List<Order> orders, List<CommodityOrder> commoditys, boolean isError) {
        String root = CrmProperties.getRoot();
        String tempPath = root + "/temp";
        File tempFilePath = new File(tempPath);
        if (!tempFilePath.exists()) tempFilePath.mkdirs();
        String filePath = tempPath + "/" + fileName;
//        fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 指定文件
        try (ExcelWriter excelWriter = EasyExcel.write(filePath).build()) {
            // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变，我这里为了方便 所以用的同一个class
            // 实际上可以一直变
            WriteSheet writeSheet = orderWriteSheet(isError);
            excelWriter.write(orders, writeSheet);
            WriteSheet writeSheet1 =commodityWriteSheet();
            excelWriter.write(commoditys, writeSheet1);
        }
        return filePath;
    }

    private WriteSheet commodityWriteSheet(){
        Set<String> excludeColumnFiledNames = new HashSet<String>();
        excludeColumnFiledNames.add("comId");
        excludeColumnFiledNames.add("productId");
        excludeColumnFiledNames.add("cost");
        excludeColumnFiledNames.add("inventory");
        excludeColumnFiledNames.add("brandId");
        excludeColumnFiledNames.add("typeId");
        excludeColumnFiledNames.add("productColor");
        excludeColumnFiledNames.add("productModel");
        excludeColumnFiledNames.add("createId");
        excludeColumnFiledNames.add("createTime");
        excludeColumnFiledNames.add("updateTime");
        excludeColumnFiledNames.add("imea");
        return EasyExcel.writerSheet(1, Constants.COMMODITY_EXCEL_SHEEL).head(Goods.class).excludeColumnFieldNames(excludeColumnFiledNames).build();
    }

    @Resource
    private CommodityOrderService commodityOrderService;
    @Resource
    GoodsService commodityService;

    private ReadSheet readExcelSheetOrderCommodity(List<CommodityOrder> errors, int[] counts, List<String> currentOrderId, List<String> sucOrderId ) {

        return EasyExcel.readSheet(1).head(CommodityOrder.class).registerReadListener(new ReadListener<CommodityOrder>() {

            public static final int BATCH_COUNT = 100;
            private List<CommodityOrder> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(CommodityOrder data, AnalysisContext analysisContext) {

                counts[0]++;
                //对data对象做数据校验
                boolean isok = data.check();
                if (!isok) {
                    errors.add(data);
                    return;
                }
                //校验通过后查看表格的集合中是否包含这个OrderId 没有就为错误数据
                isok = currentOrderId.contains(data.getOrderId());
//                if (!isok) {
//                    return;
//                }
                if (!isok) {
                    data.setErrorMsg("订单编号不存在");
                    errors.add(data);
                    return;
                }
                isok = sucOrderId.contains(data.getOrderId());
                if (!isok){
                    data.setErrorMsg("该编号订单页出错");
                    errors.add(data);
                    return;
                }

                //全部通过
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                saveData();
            }

            private void saveData() {
                boolean b = commodityOrderService.saveBatch(cachedDataList);
                if (!b) {
                    errors.addAll(cachedDataList);
                } else {
                    counts[1] += cachedDataList.size();
                }
            }
        }).build();
    }

}

